package com.googlecode.monitoring.ws.dao;

import com.googlecode.monitoring.dao.AgentStatusDao;
import com.googlecode.monitoring.dao.XmlUtils;
import com.googlecode.monitoring.exception.DataDaoException;
import com.googlecode.monitoring.model.ItemToCheck;
import com.googlecode.monitoring.model.Person;
import com.googlecode.monitoring.ws.model.WsItemToCheck;
import org.apache.log4j.Logger;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Repository;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.FileNotFoundException;
import java.util.*;


@Repository
public class XmlWsAgentStatusDao implements AgentStatusDao {

    private Logger log = Logger.getLogger(XmlWsAgentStatusDao.class);

    private Resource xmlWsStatusResource;

    private XmlUtils xmlUtils;

    private WsAgentXmlUtils wsAgentXmlUtils;
    private SchedulerFactoryBean schedulerFactoryBean;    

    @Autowired(required = false)
    public void setSchedulerFactoryBean(SchedulerFactoryBean schedulerFactoryBean) {
        this.schedulerFactoryBean = schedulerFactoryBean;
    }

    @Autowired
    public void setWsAgentXmlUtils(WsAgentXmlUtils wsAgentXmlUtils) {
        this.wsAgentXmlUtils = wsAgentXmlUtils;
    }

    @Autowired
    public void setXmlUtils(XmlUtils xmlUtils) {
        this.xmlUtils = xmlUtils;
    }

    @Autowired
    public void setXmlWsStatusResource(Resource xmlWsStatusResource) {
        this.xmlWsStatusResource = xmlWsStatusResource;
    }

    /**
     * save status of check job to xml file
     *
     * @param item object of job
     * @return boolean false if something wrong
     */
    @Override
    public boolean saveItemToCheckReport(ItemToCheck item) {
        try {
            Set<ItemToCheck> reportItems;
            try {
                reportItems = new HashSet<ItemToCheck>(getAllItemsFromCheckReport());
            }catch (FileNotFoundException e) {
                reportItems = new HashSet<ItemToCheck>(1);
            }
            reportItems.remove(item);
            reportItems.add(item);
            saveWsItemsToCheckReport(reportItems);
            return true;
        }
        catch (Exception e) {
            log.error(" error with saving item = " + item,e);
            throw new DataDaoException(" error with saving item = " + item,e);
        }
    }

    private void saveWsItemsToCheckReport(Collection<ItemToCheck> reportItems) throws Exception {
        Document doc = xmlUtils.createDoc();
        Element root = (Element) doc.getElementsByTagName("document").item(0);
        for (ItemToCheck wsItem : reportItems) {
            root.appendChild(buildWsItemReportElement(doc, wsItem));
        }
        xmlUtils.saveDocument(doc, xmlWsStatusResource);
    }

    private Element buildWsItemReportElement(Document doc, ItemToCheck wsItem) throws Exception{
        Element itemEl = doc.createElement("item");
        Element idEl =  doc.createElement("id");
        Element nameEl = doc.createElement("name");
        Element descrEl = doc.createElement("description");
        Element stateEl = doc.createElement("state");
        Element lastCheckTimeEl = doc.createElement("lastCheckTime");
        Element nextCheckTimeEl = doc.createElement("nextCheckTime");

        idEl.setTextContent(wsItem.getId());
        nameEl.setTextContent(wsItem.getName());
        descrEl.setTextContent(wsItem.getDescr());
        if (wsItem.getCurrentStateName() != null) {
            stateEl.setAttribute("name", wsItem.getCurrentStateName().getName());
        }
        lastCheckTimeEl.setTextContent(wsItem.getLastCheckTime()==null?"":wsItem.getLastCheckTime().toString(XmlUtils.dateTimeFormat));
        nextCheckTimeEl.setTextContent(xmlUtils.getNextCheckTime(wsItem, schedulerFactoryBean).toString(XmlUtils.dateTimeFormat));

        itemEl.appendChild(idEl);
        itemEl.appendChild(nameEl);
        itemEl.appendChild(descrEl);
        itemEl.appendChild(stateEl);
        itemEl.appendChild(lastCheckTimeEl);
        itemEl.appendChild(nextCheckTimeEl);

        return itemEl;
    }   

    /**
     * read status of check data from xml file
     *
     * @return list of DbItemToCheck object instance
     * @throws DataDaoException
     *          exception
     */
    @Override
    public List<ItemToCheck> getAllItemsFromCheckReport() throws DataDaoException, FileNotFoundException {
        return getXmlDataItems(xmlWsStatusResource);  
    }

    /**
     * search item by id
     *
     * @param itemToCheck
     * @return list of DbItemToCheck object instance
     * @throws DataDaoException
     */
    @Override
    public ItemToCheck getItemFromCheckReport(ItemToCheck itemToCheck) throws DataDaoException {
        try {
            Document doc = xmlUtils.getDocumentFromResource(xmlWsStatusResource,true);
            XPath xpath = XPathFactory.newInstance().newXPath();
            XPathExpression exprChildOfItem = xpath.compile("document/item[id='" + itemToCheck.getId() + "']");
            Node wsItemNode = (Node) exprChildOfItem.evaluate(doc, XPathConstants.NODE);
            if(wsItemNode !=  null){
                return itemToCheck;
            }
        }catch (Throwable e) {
            throw new DataDaoException(e);
        }
        return null;
    }

	public Resource getStatusResource() {
		return xmlWsStatusResource;
	}

    private List<ItemToCheck> getXmlDataItems(Resource xmlResource) throws DataDaoException, FileNotFoundException {
        try {
            Document doc = xmlUtils.getDocumentFromResource(xmlResource);

            Set<Person> alertPersons = null;
            Element alertsNode = (Element) doc.getElementsByTagName("alerts").item(0);
            if (alertsNode != null) {
                NodeList alertPersonsNodeList = alertsNode.getElementsByTagName("person");
                alertPersons = xmlUtils.buildPersons(alertPersonsNodeList);
            }

            NodeList nodes = doc.getElementsByTagName("item");
            List<ItemToCheck> items = new ArrayList<ItemToCheck>();
            log.debug("read " + nodes.getLength() + " nodes item");
            for (int i = 0; i < nodes.getLength(); i++) {
                Node xmlItem = nodes.item(i);
                if (xmlItem.getNodeType() == Node.ELEMENT_NODE) {
                    items.add(getXmlDataItemFromSatus((Element) xmlItem, alertPersons));
                }
            }

            return items;
        }
        catch (FileNotFoundException e) {
            throw new FileNotFoundException(e.getMessage());
        }
        catch (Throwable e) {
            throw new DataDaoException(e);
        }
    }

    private WsItemToCheck getXmlDataItemFromSatus(Element xmlItemElmnt, Set<Person> alertPersons){
        WsItemToCheck wsItemToCheck = wsAgentXmlUtils.buildInstanceToCheck(xmlItemElmnt, alertPersons);
        Node lastCheckNode = xmlItemElmnt.getElementsByTagName("lastCheckTime").item(0);
		Node nextCheckNode = xmlItemElmnt.getElementsByTagName("nextCheckTime").item(0);
        wsItemToCheck.setLastCheckTime(DateTimeFormat.forPattern(XmlUtils.dateTimeFormat).parseDateTime(lastCheckNode.getTextContent()));
        wsItemToCheck.setNextCheckTime(DateTimeFormat.forPattern(XmlUtils.dateTimeFormat).parseDateTime(nextCheckNode.getTextContent()));
        return wsItemToCheck;
    }

}
